// @ts-nocheck
import path from "path";
import { Vue, Konva, fs } from "../../../lib";
import { AbortType, Action, Composite, Decorator, Condition, ParentNode } from "../../runtime/node";
import { nodeClsMap } from "../../runtime/core/decorator";
import { buildTree, preOrder } from "../../runtime/core/utils";

// 画布宽高
const CANVAS_WIDTH = 2000;
const CANVAS_HEIGHT = 1400;
// 节点box属性
const BOX_WIDTH = 100;
// const BOX_HEIGHT = 64; //height通过getBoxHeight方法动态计算
const BOX_FILL = "#484848";
const BOX_FILL_ACTIVE = "#E8B116";
const BOX_BORDER_WIDTH = 4;
// 节点main属性
const MAIN_GAP = 4;
const MAIN_WIDTH = BOX_WIDTH - MAIN_GAP * 2;
const MAIN_HEIGHT = 42;
const MAIN_FILL = "#2b2b2b";
const MAIN_BORDER_COLOR = "#121212";
const MAIN_BORDER_WIDTH = 1;
const MAIN_BORDER_RADIUS = 2;
// 节点panel属性
const PANEL_WIDTH = BOX_WIDTH - MAIN_GAP * 2;
const PANEL_HEIGHT = 14;
const PANEL_FILL = "#2C2C2C";
const PANEL_BORDER_RADIUS = 2;
// 删除节点圆圈属性
const REMOVE_RADIUS = 8;
const REMOVE_PADDING = 5;
const REMOVE_CIRCLE_FILL = "#484848";
const REMOVE_LINE_FILL = "#CCCCCC";
const REMOVE_LINE_WIDTH = 2;
// 中断类型属性
const ABORT_RADIUS = 5;
const ABORT_FILL = "#642EA4";
const ABORT_LINE_SIZE = 4;
// 箭头属性
const ARROW_FILL = "#F2F2F2";
const ARROW_SIZE = 6;
const ARROW_LINE_SIZE = 2;
// 辅助线间隔
const LINE_GAP = 20;
// 文本属性
const TEXT_FILL = "#CCCCCC";
const NAME_SIZE = 14;
const TYPE_SIZE = 12;
const TEXT_PADDING = 5;
// 边框半径
const BORDER_RADIUS = 6;
// Root节点属性
const ROOT_X = (CANVAS_WIDTH - BOX_WIDTH) / 2;
const ROOT_Y = 300;

let stage: any;
let baseLayer: any;
let bgGroup: any;
let staticArrowGroup: any;
let dynamicArrowGroup: any;
let rootGroup: any;
let nodeGroup: any;
let selectionGroup: any;
// 拖拽panel时生成的动态箭头
let dynamicArrow: any;
// 当前框选的box（因为计算碰撞只能跟shape碰撞，不能跟group）
let selectedBoxes = [];

// 移动防抖存储相关;
let SAVE_DEBOUNCE_TIMER: NodeJS.Timeout | null = null;
const DEBOUNCE_TIME = 200;

// 获取父类构造函数(ts:constructor === class)
const getParentCls = (cls: Function) => cls && cls.prototype.__proto__?.constructor;
// 生成节点uuid
const uuid = () => Editor.Utils.UUID.generate();
// 防止越界
const clamp = (value: number, max: number, min: number) => (value < min ? min : value > max ? max : value);
// 是否命中
const hit = (x: number, y: number, targetX: number, targetY: number, targetW: number, targetH: number) =>
  x > targetX && y > targetY && x < targetX + targetW && y < targetY + targetH;

// 时间格式化
const getTime = () => {
  const addZero = (value: number) => (value < 10 ? "0" + value : value);
  const date = new Date();
  let year = date.getFullYear();
  let month = addZero(date.getMonth() + 1);
  let weekday = addZero(date.getDate());
  let hour = addZero(date.getHours());
  let minute = addZero(date.getMinutes());
  let second = addZero(date.getSeconds());

  return year + "-" + month + "-" + weekday + " " + hour + ":" + minute + ":" + second;
};

const BTreeCompName = "BehaviorTree";
/***
 * 通过btclass装饰器收集到所有节点类型，然后把节点按类型分组，组成以下结构
 * [{
      id: NodeCategory.Composite,
      items: [ { type: NodeType.Selector }, { type: NodeType.Selector } ],
      expand: true}];
 */
const nodeCategory = ((nodeClsMap) => {
  let temp: Map<string, Array<any>> = new Map([
    [Composite.name, []],
    [Decorator.name, []],
    [Condition.name, []],
    [Action.name, []],
  ]);
  let clsList = [...nodeClsMap.values()];
  for (const cls of clsList) {
    // 获取node的父类cls名字
    const parentCls = getParentCls(cls);
    if (parentCls) {
      const parentClsName = parentCls.name;
      temp.get(parentClsName)?.push({
        type: cls.name,
      });
    }
  }

  const result = [...temp.entries()].map(([id, items]) => ({
    id,
    items,
    expand: true,
  }));

  return result;
})(nodeClsMap);

/***
 * 获取所有parentNode类型的节点
 */
const ParentNodes = [...nodeClsMap.values()]
  .filter((cls) => {
    let parentCls = getParentCls(cls);
    while (parentCls) {
      if (parentCls.name === ParentNode.name) {
        return true;
      }

      parentCls = getParentCls(parentCls);
    }

    return false;
  })
  .map((cls) => cls.name);

/***
 * 获取所有Composite类型的节点
 */
const CompositeNodes = [...nodeClsMap.values()]
  .filter((cls) => {
    let parentCls = getParentCls(cls);
    while (parentCls) {
      if (parentCls.name === Composite.name) {
        return true;
      }

      parentCls = getParentCls(parentCls);
    }

    return false;
  })
  .map((cls) => cls.name);

const activeCursor = () => {
  stage && (stage.container().style.cursor = "pointer");
};
const defaultCursor = () => {
  stage && (stage.container().style.cursor = "default");
};

const component = Vue.extend({
  template: fs.readFileSync(path.join(__dirname, "../../../src/panels/static/template/vue/app.html"), "utf-8"),
  $: {
    tree: "#tree",
    scroll: "#scroll",
  },
  filters: {
    toUpperCase(value) {
      if (!value) {
        return "";
      }

      const str = value.toString();
      return str.charAt(0).toUpperCase() + str.slice(1);
    },
  },
  data() {
    return {
      onStart: "onStart",
      onUpdate: "onUpdate",
      onEnd: "onEnd",
      // 为了让模板访问,
      AbortType,
      panels: ["Nodes", "Inspector"],
      // 当前选中的panel
      currentPanel: 0, //索引
      // 节点面板
      nodeCategory,
      // json资源列表
      assets: [],
      // 当前选中的json资源
      currentAsset: null,
      // 当前选中的节点
      currentNode: null,
      // 事件相关节点查询的组件和方法信息
      /***
       * 结构如下 {
       *    [lifecycle]:[{ uuid, methods,compName }]
       *    }
       * }
       */
      nodeCompMethodInfo: {},
      logs: [],
      maskText: "",
    };
  },
  computed: {
    nodes() {
      if (this.currentAsset) {
        return this.currentAsset.content.nodes;
      }
      return [];
    },
    nodeMap() {
      const map = {};
      for (const node of this.nodes) {
        map[node.id] = node;
      }

      return map;
    },
    // key是节点id，value是前序索引
    preOrderIndexMap() {
      const root = buildTree(this.nodes);
      if (!root) {
        return {};
      }

      const temp = preOrder(root);
      const map = temp.reduce((total, item, index) => {
        total[item.id] = index;
        return total;
      }, {});

      return map;
    },
    abortTypeList() {
      // 解决数字枚举编译成js以后，生成两份key的问题
      return Object.entries(this.AbortType)
        .filter((e) => !Number.isInteger(Number(e[0])))
        .map((e) => ({ label: e[0], value: e[1] }));
    },
    lifeCycleComponents() {
      return [this.onStart, this.onUpdate, this.onEnd].reduce((total, lifeCycle) => {
        let res = [];
        if (this.currentNode) {
          const isSelectNode = Boolean(this.currentNode.event[lifeCycle].node);
          if (isSelectNode) {
            res = this.nodeCompMethodInfo[lifeCycle] || [];
          }
        }

        total[lifeCycle] = res;
        return total;
      }, {});
    },
    lifeCycleMethods() {
      return [this.onStart, this.onUpdate, this.onEnd].reduce((total, lifeCycle) => {
        let res = [];
        if (this.currentNode) {
          const compUuid = this.currentNode.event[lifeCycle]?.comp;
          if (compUuid && this.nodeCompMethodInfo[lifeCycle]) {
            res = this.nodeCompMethodInfo[lifeCycle].find((e) => e.uuid === compUuid)?.methods;
          }
        }

        total[lifeCycle] = res;
        return total;
      }, {});
    },
  },
  async mounted() {
    this.init();
  },
  methods: {
    /***
     * 初始化相关
     */
    async init() {
      // 初始化画布
      this.initCanvas();
      // 根据当前选中节点初始化行为树
      await this.initSelection();
      this.backRoot();
    },
    initCanvas() {
      // 初始化舞台
      stage = new Konva.Stage({
        container: this.$refs.tree,
        width: CANVAS_WIDTH,
        height: CANVAS_HEIGHT,
      });
      // 基础层，目前只有一层，每层都会生成一个canvas
      baseLayer = new Konva.Layer();

      // 背景组
      bgGroup = this.generateBg();
      baseLayer.add(bgGroup);

      // 静态箭头层
      staticArrowGroup = new Konva.Group();
      baseLayer.add(staticArrowGroup);

      // 动态箭头层（用户手动拉出来的箭头）
      dynamicArrowGroup = this.generateDynamicArrowGroup();
      baseLayer.add(dynamicArrowGroup);

      // Root组
      rootGroup = this.generateRoot();
      baseLayer.add(rootGroup);

      // 节点层
      nodeGroup = new Konva.Group();
      baseLayer.add(nodeGroup);

      // 框选层
      selectionGroup = this.generatesSelectionGroup();
      baseLayer.add(selectionGroup);

      // 层添加到舞台
      stage.add(baseLayer);
    },
    // 获取json文件
    async initAssets() {
      const behaviorTreeComponentUuids = [];
      const dfs = (node) => {
        if (!node) {
          return;
        }

        for (const comp of node.components) {
          if (comp.type === BTreeCompName) {
            behaviorTreeComponentUuids.push(comp.value);
            break;
          }
        }
        for (const item of node.children) {
          dfs(item);
        }
      };
      // 获取场景节点树
      const sceneNode = await Editor.Message.request("scene", "query-node-tree");

      // 收集场景上所有BehaviorTree组件uuid
      dfs(sceneNode);

      // 收集BehaviorTree组件上的json路径
      const rawUrls = await Promise.all(
        behaviorTreeComponentUuids.map((uuid) =>
          Editor.Message.request("scene", "execute-component-method", {
            uuid: uuid,
            name: "getAssetUrl",
          })
        )
      );

      // 过滤空的并去重
      const urls = [...new Set(rawUrls.filter(Boolean))];
      // 根据url获取所有json文件信息
      const assets = await Promise.all(urls.map((url) => Editor.Message.request("asset-db", "query-asset-info", url)));
      this.assets = assets.map(({ name, source, file }) => ({ name, url: source, file: file, content: "" }));
    },
    async initSelection() {
      // 切换节点时清空全局数据
      selectedBoxes = [];
      SAVE_DEBOUNCE_TIMER = null;
      // 找到当前选中的节点
      const node = await Editor.Message.request("scene", "query-node", Editor.Selection.getSelected("node"));

      // 未选中节点或者选中的是场景节点（场景节点不能添加组件）
      if (!node?.__comps__) {
        this.maskText = "请选中一个非场景根节点非空名称节点来开始行为树制作";
        return;
      }

      // 找到BehaviorTree组件
      const index = node.__comps__.findIndex((v: any) => v.type === BTreeCompName);
      if (index === -1) {
        this.maskText = "要制作行为树，需要先为当前节点添加行为树组件";
        return;
      }

      const comp = node.__comps__[index];
      //   调用BehaviorTree组件上的方法
      const url = await Editor.Message.request("scene", "execute-component-method", {
        uuid: comp.value.uuid.value,
        name: "getAssetUrl",
      });

      // JSON文件不存在
      if (!url) {
        this.maskText = "当前行为树组件缺少JSON资源，点击BehaviorEditor组件按钮即可创建";
        return;
      }

      this.maskText = "";

      // 选中此文件，设置好currentAsset才能把组件nodes数据同步到JSON
      this.handleSelectAsset(url);
    },
    async handleSelectAsset(url) {
      // 实时获取当前场景所有behaviorTree组件上的json文件
      await this.initAssets();
      const json = this.assets.find((e) => e.url === url);
      if (!json) {
        this.showWarn("JSON文件不存在");
        return;
      }

      if (this.currentAsset?.url === json?.url) {
        return;
      }

      this.currentAsset = json;

      try {
        const content = await fs.readJSONSync(json.file);
        this.currentAsset.content = content;
        this.render();
      } catch (e) {
        if (e instanceof SyntaxError) {
          // JSON文件语法异常的情况下，初始化内容
          this.showWarn("JSON文件内容初始化成功");
          const content = { nodes: [] };
          await fs.writeJSONSync(json.file, content);
          this.currentAsset.content = content;
          this.render();
        } else {
          // 输出其他异常
          this.showWarn(e);
        }
      }
    },

    handlePanelChange(value) {
      this.currentPanel = value;
    },
    handleNameChange(value) {
      if (this.currentNode) {
        this.currentNode.name = value;
        // 需要立刻渲染节点，并防抖存储
        this.render(true, true);
      }
    },
    handleAbortTypeChange(value) {
      if (this.currentNode) {
        this.currentNode.abortType = Number(value);
        this.render();
      }
    },
    async handleEventNodeChange(lifeCycle, uuid = "", shouldSave = true) {
      if (!this.currentNode) {
        this.showWarn("当前节点不存在");
        return;
      }

      this.currentNode.event[lifeCycle].node = uuid;
      if (uuid) {
        await this.getNodeCompMethods(lifeCycle, uuid);
      } else {
        this.currentNode.event[lifeCycle].comp = "";
        this.currentNode.event[lifeCycle].method = "";
        // 事件参数就不手动清空了
        // this.currentNode.event[lifeCycle].data = "";
      }
      // 点击canvas某个节点的时候，会触发handleEventNodeChange获取场景节点数据，此时不保存数据
      if (shouldSave) {
        await this.saveAsset();
      }
    },
    async getNodeCompMethods(lifeCycle, uuid) {
      const [nodeInfo, compMethodInfo] = await Promise.all([
        Editor.Message.request("scene", "query-node", uuid),
        Editor.Message.request("scene", "query-component-function-of-node", uuid),
      ]);
      if (!nodeInfo || !compMethodInfo) {
        this.showWarn("节点信息不存在");
        return;
      }
      /***
       * compMethodInfo的组件信息只有组件名，需要获取组件的uuid
       * 组成这样的格式
       */
      const compsMethods = nodeInfo.__comps__.map((comp) => {
        const name = comp.type;
        const uuid = comp.value.uuid.value;
        const methods = compMethodInfo[name];
        return {
          name,
          uuid,
          methods,
        };
      });
      this.nodeCompMethodInfo = {
        ...this.nodeCompMethodInfo,
        [lifeCycle]: compsMethods,
      };
    },
    async handleEventCompChange(lifeCycle, uuid) {
      this.currentNode.event[lifeCycle].comp = uuid;
      if (!uuid) {
        this.currentNode.event[lifeCycle].method = "";
      }
      await this.saveAsset();
    },
    async handleEventMethodChange(lifeCycle, method) {
      this.currentNode.event[lifeCycle].method = method;
      await this.saveAsset();
    },
    async handleEventDataChange(lifeCycle, data) {
      this.currentNode.event[lifeCycle].data = data;
      await this.saveAsset();
    },
    async saveAsset() {
      const file = this.currentAsset?.file;
      if (!file) {
        this.showWarn("数据存储失败，未指定JSON");
        return;
      }
      const content = this.currentAsset.content;

      // 修改json文件
      fs.writeJSONSync(file, content);
    },

    /***
     * 渲染画布
     * renderNode：拖拽的时候不渲染节点
     * debounceSave：防抖存储，用在移动和高频input中
     */
    render(renderNode = true, debounceSave = false) {
      staticArrowGroup.destroyChildren();
      for (const node of this.nodes) {
        this.renderArrow(node);
      }

      clearTimeout(SAVE_DEBOUNCE_TIMER);
      // 渲染节点
      if (renderNode) {
        nodeGroup.destroyChildren();
        for (const node of this.nodes) {
          this.renderNode(node);
        }
      }

      // 防抖存储
      if (debounceSave) {
        SAVE_DEBOUNCE_TIMER = setTimeout(() => {
          SAVE_DEBOUNCE_TIMER = null;
          this.saveAsset();
        }, DEBOUNCE_TIME);
      } else {
        this.saveAsset();
      }
    },
    renderNode(node) {
      // 节点组
      const wrapper = this.generateWrapper(node);
      // 节点盒子
      const box = this.generateBox(node);
      // 节点主要内容
      const main = this.generateMain(node);
      // 节点文本
      const [name, type] = this.generateText(node);
      // 节点上下面板
      const [panelStatic, panelDynamic] = this.generatePanel(node);
      // 中断类型icon
      const abortGroup = this.generateAbort(node);
      // 移除节点按钮
      const removeGroup = this.generateRemove(node);
      // 断开箭头按钮
      const breakGroup = this.generateBreak(node);

      wrapper.add(box);
      wrapper.add(main);
      wrapper.add(name);
      wrapper.add(type);
      // 继承ParentNode类型的节点才能显示设置子节点面板
      if (ParentNodes.includes(node.type)) {
        wrapper.add(panelStatic);
        wrapper.add(panelDynamic);
      }
      wrapper.add(abortGroup);
      wrapper.add(removeGroup);
      wrapper.add(breakGroup);

      removeGroup.visible(false);
      breakGroup.visible(false);
      const parentNode = this.getParentNode(node);
      // 有父节点或者是Root节点的时候，渲染break按钮
      const showBreak = Boolean(parentNode || node.isRoot);

      wrapper.on("mouseover", () => {
        !removeGroup.visible() && removeGroup.visible(true);
        !breakGroup.visible() && showBreak && breakGroup.visible(true);
      });
      wrapper.on("mouseout", () => {
        removeGroup.visible() && removeGroup.visible(false);
        breakGroup.visible() && showBreak && breakGroup.visible(false);
      });

      nodeGroup.add(wrapper);
    },
    /***
     * 渲染从父节点指向子节点的箭头
     * getArrowStartXY传入父节点，getArrowEndXY传入子节点
     */
    renderArrow(node) {
      const commonCfg = {
        pointerLength: ARROW_SIZE,
        pointerWidth: ARROW_SIZE,
        fill: ARROW_FILL,
        stroke: ARROW_FILL,
        strokeWidth: ARROW_LINE_SIZE,
      };
      // isRoot标识为true的节点，多渲染一根从Root指向该节点的箭头
      if (node.isRoot) {
        const arrowStartXY = this.getArrowStartXY({
          x: ROOT_X,
          y: ROOT_Y,
        });
        const arrowEndXY = this.getArrowEndXY(node);
        const arrow = new Konva.Arrow({
          ...arrowStartXY,
          points: [0, 0, arrowEndXY.x - arrowStartXY.x, arrowEndXY.y - arrowStartXY.y],
          ...commonCfg,
        });
        staticArrowGroup.add(arrow);
      }
      for (const childId of node.children) {
        const childNode = this.nodeMap[childId];
        const arrowStartXY = this.getArrowStartXY(node);
        const arrowEndXY = this.getArrowEndXY(childNode);
        const arrow = new Konva.Arrow({
          ...arrowStartXY,
          points: [0, 0, arrowEndXY.x - arrowStartXY.x, arrowEndXY.y - arrowStartXY.y],
          ...commonCfg,
        });
        staticArrowGroup.add(arrow);
      }
    },
    addNode(type) {
      const node = {
        id: uuid(),
        name: type,
        type,
        abortType: this.AbortType.None,
        x: (CANVAS_WIDTH - BOX_WIDTH) / 2,
        y: ROOT_Y + 100,
        isRoot: false,
        children: [],
        event: {
          onStart: { node: "", comp: "", method: "", data: "" },
          onUpdate: { node: "", comp: "", method: "", data: "" },
          onEnd: { node: "", comp: "", method: "", data: "" },
        },
      };
      this.nodes.push(node);
      this.render();
    },
    removeNode(node) {
      const removeId = node.id;
      // 倒序删除
      for (let i = this.nodes.length - 1; i >= 0; i--) {
        const item = this.nodes[i];
        // 删除当前节点
        if (item.id === removeId) {
          this.nodes.splice(i, 1);
        } else {
          // 删除关系节点
          item.children = item.children.filter((childId) => childId !== removeId);
        }
      }
      this.render();
      if (this.currentNode === node) {
        this.currentNode = null;
      }
      defaultCursor();
    },
    removeParent(node) {
      // 根节点的话，重置isRoot标识
      if (node.isRoot) {
        node.isRoot = false;
        this.render();
        return;
      }
      const parentNode = this.getParentNode(node);
      if (parentNode) {
        const index = parentNode.children.findIndex((childId) => childId === node.id);
        index > -1 && parentNode.children.splice(index, 1);
        this.render();
      }
      defaultCursor();
    },
    /***
     * 设置子节点
     * node参数是父节点，通过e获取需要设置的子节点
     */
    setChild(e, node) {
      const { x, y } = stage.getPointerPosition();
      // PS：hitNode不可能是Root UI节点，因为Root UI是虚构的，不存在 this.nodes中
      const hitNode = this.nodes.find((node) => {
        const targetX = node.x;
        const targetY = node.y;
        return hit(x, y, targetX, targetY, BOX_WIDTH, this.getBoxHeight(node));
      });

      // 没有命中节点
      if (!hitNode) {
        this.showWarn("未命中节点");
        return;
      }

      // 从Root节点拉出来的箭头，命中普通节点
      if (!node.id) {
        // 命中节点已经是Root节点
        if (hitNode.isRoot) {
          this.showWarn("命中节点已经是Root节点");
          return;
        }
        // 删除命中节点原来的关系
        this.removeParent(hitNode);
        // 重置所有isRoot标识
        this.nodes.map((e) => (e.isRoot = false));
        // 设置命中节点Root标识
        hitNode.isRoot = true;
        this.render();
        // 普通节点拉出来的箭头命中普通节点
      } else {
        // 命中节点的所有子孙节点（包括命中节点）包含当前节点，会造成循环
        const dirty = this.getAllChildrenNode(hitNode).some((v) => v.id === node.id);
        if (dirty) {
          this.showWarn("存在循环");
          return;
        }
        // 命中节点已是当前节点子节点
        const parentHitNode = this.getParentNode(hitNode);
        if (parentHitNode?.id === node.id) {
          this.showWarn("命中节点已是当前节点子节点");
          return;
        }

        // 删除命中节点原来的关系
        this.removeParent(hitNode);
        // 设置新关系
        node.children.push(hitNode.id);
        // 根据x坐标对子节点排序
        this.sortNodeChildren(node);
        this.render();
      }
    },
    getParentNode(node) {
      return this.nodes.find((v) => v.children.includes(node.id));
    },
    sortNodeChildren(node) {
      node.children.sort((a, b) => this.nodeMap[a].x - this.nodeMap[b].x);
    },
    // 获取所有子孙节点
    getAllChildrenNode(node) {
      if (!node) {
        return [];
      }
      let result = [];
      const dfs = (node) => {
        if (!node) {
          return;
        }

        result.push(node);
        for (const nodeId of node.children) {
          const child = this.nodeMap[nodeId];
          dfs(child);
        }
      };

      dfs(node);

      return result;
    },
    /***
     * 移动节点，把xy保存到数据中，并重新渲染箭头
     */
    handleNodeMove(shape) {
      const id = shape.attrs.id;
      if (!id) {
        return;
      }
      const node = this.nodeMap[id];
      node.x = shape.attrs.x;
      node.y = shape.attrs.y;
      const parentNode = this.getParentNode(node);
      // 排序
      if (parentNode) {
        this.sortNodeChildren(parentNode);
      }
      this.render(false, true);
    },
    getArrowStartXY(node) {
      return {
        x: node.x + BOX_WIDTH / 2,
        y: node.y + this.getBoxHeight(node) - PANEL_HEIGHT / 2,
      };
    },
    getArrowEndXY(childNode) {
      const childCenter = childNode.x + BOX_WIDTH / 2;
      return {
        x: childCenter,
        y: childNode.y - 2,
      };
    },
    resetBoxBorder() {
      for (const child of nodeGroup.children) {
        child.children[0].setAttrs({
          stroke: BOX_FILL,
        });
      }
    },
    getNodeCenter(node) {
      return {
        x: node.x + BOX_WIDTH / 2,
        y: node.y + this.getBoxHeight(node) / 2,
      };
    },
    /***
     * 设置背景和辅助线
     */
    generateBg() {
      const group = new Konva.Group();
      const bg = new Konva.Rect({
        x: 0,
        y: 0,
        fill: "#262626",
        width: CANVAS_WIDTH,
        height: CANVAS_HEIGHT,
        id: "bg", //可以通过api find()找到这个节点
      });
      group.add(bg);
      // 辅助线
      for (let i = 0; i < CANVAS_WIDTH / LINE_GAP; i++) {
        const y = i * LINE_GAP;
        const x = i * LINE_GAP;
        if (i % 8 === 0) {
          const lineCfg = {
            stroke: "#000000",
            strokeWidth: 1,
          };
          const rowLine = new Konva.Line({
            ...lineCfg,
            points: [0, y, CANVAS_WIDTH, y],
          });
          const columnLine = new Konva.Line({
            ...lineCfg,
            points: [x, 0, x, CANVAS_HEIGHT],
          });
          group.add(rowLine);
          group.add(columnLine);
        } else {
          const lineCfg = {
            stroke: "#343434",
            strokeWidth: 1,
          };
          const rowLine = new Konva.Line({
            ...lineCfg,
            points: [0, y, CANVAS_WIDTH, y],
          });
          const columnLine = new Konva.Line({
            ...lineCfg,
            points: [x, 0, x, CANVAS_HEIGHT],
          });
          group.add(rowLine);
          group.add(columnLine);
        }
      }

      return group;
    },
    generateDynamicArrowGroup() {
      const group = new Konva.Group();
      dynamicArrow = new Konva.Arrow({
        points: [0, 0, 0, 30],
        pointerLength: ARROW_SIZE,
        pointerWidth: ARROW_SIZE,
        fill: ARROW_FILL,
        stroke: ARROW_FILL,
        strokeWidth: ARROW_LINE_SIZE,
        visible: false,
      });
      // 动态箭头离开了canvas,松手的时候隐藏箭头
      stage.on("mouseleave", () => {
        dynamicArrow.visible(false);
      });
      group.add(dynamicArrow);
      return group;
    },
    generatesSelectionGroup() {
      const group = new Konva.Group();
      const selection = new Konva.Rect({
        fill: "rgba(0,0,0,0.3)",
        visible: false,
      });
      let x1, y1, x2, y2;

      stage.on("mousedown", (e) => {
        e.evt.preventDefault();
        if (e.evt.button === 1) {
          return;
        }
        const source = e.target;
        const target = stage.find("#bg")[0];
        // 点击的是背景,则开始框选逻辑
        if (source !== target) {
          return;
        }
        x1 = x2 = stage.getPointerPosition().x;
        y1 = y2 = stage.getPointerPosition().y;

        selection.visible(true);
        selection.width(0);
        selection.height(0);
      });

      stage.on("mousemove", (e) => {
        e.evt.preventDefault();
        // 按下中间则拖拽
        if (e.evt.buttons === 4) {
          const movementX = e.evt.movementX;
          const movementY = e.evt.movementY;
          this.$refs.scroll.scrollBy(-movementX, -movementY);
          return;
        }
        if (!selection.visible()) {
          return;
        }
        e.evt.preventDefault();
        x2 = stage.getPointerPosition().x;
        y2 = stage.getPointerPosition().y;

        selection.setAttrs({
          x: Math.min(x1, x2),
          y: Math.min(y1, y2),
          width: Math.abs(x2 - x1),
          height: Math.abs(y2 - y1),
        });
      });

      stage.on("mouseup", (e) => {
        e.evt.preventDefault();
        // 中键
        if (e.evt.button === 1) {
          return;
        }
        // 所有点击都会冒泡上来
        if (!selection.visible()) {
          return;
        }
        const boxes = stage.find(".box");
        const clientRect = selection.getClientRect();
        selectedBoxes = boxes.filter((box) =>
          Konva.Util.haveIntersection(clientRect, box.getClientRect({ skipShadow: true }))
        );
        this.resetBoxBorder();
        if (selection.width() !== 0 && selection.height() !== 0 && selectedBoxes.length) {
          selectedBoxes.forEach((node) => {
            node.stroke(BOX_FILL_ACTIVE);
          });
        } else {
          // 没有框选到目标重置激活状态
          this.currentNode = null;
          this.handlePanelChange(0);
          // 每次点击画布都重新渲染，保证最新
          this.render();
        }
        selection.visible(false);
      });

      group.add(selection);
      return group;
    },
    /***
     * Root节点本身并没有实际意义，只是会指向节点中isRoot标识为true的节点
     */
    generateRoot() {
      // 静态Root节点单独一组，因为要盖住staticArrowGroup，而且nodeGroup每次渲染都会清空
      const group = new Konva.Group();
      // 根节点虚构一个node结构，node没有id的话，就是Root节点
      const node = {
        x: ROOT_X,
        y: ROOT_Y,
      };
      const wrapper = new Konva.Group({
        ...node,
      });

      const box = new Konva.Rect({
        x: 0,
        y: 0,
        width: BOX_WIDTH,
        height: this.getBoxHeight(node),
        fill: BOX_FILL,
        stroke: BOX_FILL,
        strokeWidth: BOX_BORDER_WIDTH,
        cornerRadius: BORDER_RADIUS,
        shadowColor: "#111111",
        shadowBlur: 16,
        shadowOffset: { x: 2, y: 2 },
      });

      const mainX = (BOX_WIDTH - MAIN_WIDTH) / 2;
      const main = new Konva.Rect({
        x: mainX,
        y: MAIN_GAP,
        width: MAIN_WIDTH,
        height: MAIN_HEIGHT,
        fill: MAIN_FILL,
        stroke: MAIN_BORDER_COLOR,
        strokeWidth: MAIN_BORDER_WIDTH,
        cornerRadius: MAIN_BORDER_RADIUS,
      });

      const name = new Konva.Text({
        x: mainX + TEXT_PADDING,
        text: "Root",
        fontSize: NAME_SIZE,
        fill: TEXT_FILL,
        width: MAIN_WIDTH,
        height: NAME_SIZE,
      });
      // 文字的大小会影响高度，所以动态设置
      name.setAttrs({
        y: MAIN_GAP * 6 - name.height(),
      });

      const [panelStatic, panelDynamic] = this.generatePanel({
        ...node,
      });

      wrapper.add(box);
      wrapper.add(main);
      wrapper.add(name);
      wrapper.add(panelStatic);
      wrapper.add(panelDynamic);
      group.add(wrapper);
      return group;
    },
    generateWrapper(node) {
      const BOX_HEIGHT = this.getBoxHeight(node);
      const wrapper = new Konva.Group({
        id: node.id, //移动面板node的时候，需要根据id标识找到node数据
        x: node.x,
        y: node.y,
        draggable: true,
        dragBoundFunc(currentNodePos) {
          return {
            x: clamp(currentNodePos.x, CANVAS_WIDTH - BOX_WIDTH, 0),
            y: clamp(currentNodePos.y, CANVAS_HEIGHT - BOX_HEIGHT, 0),
          };
        },
      });

      wrapper.on("dragmove", (e) => {
        wrapper.zIndex(nodeGroup.children.length - 1);
        const movementX = e.evt.movementX;
        const movementY = e.evt.movementY;
        const curMoveShape = e.target;
        // selectedBoxes里存的是box，curMoveShape(e.target)是wrapper
        const selectWrapper = selectedBoxes.map((e) => e.parent);

        const isCurShapeInSelectWrapper = selectWrapper.some((e) => e.attrs.id === curMoveShape.attrs.id);
        // 当前选中的节点是否在框选名单内
        if (isCurShapeInSelectWrapper) {
          // 在的话，所有节点同时移动
          for (const item of selectWrapper) {
            // 其他在框选中的节点，跟着被指定节点移动
            if (item.attrs.id !== curMoveShape.attrs.id) {
              item.setAttrs({
                x: item.attrs.x + movementX,
                y: item.attrs.y + movementY,
              });
            }
            this.handleNodeMove(item);
          }
        } else {
          this.handleNodeMove(curMoveShape);
        }
      });
      wrapper.on("click", (e) => {
        // 只支持左键选中
        if (e.evt.button !== 0) {
          return;
        }

        this.resetBoxBorder();
        selectedBoxes = [];
        this.currentNode = node;
        this.handlePanelChange(1);
        // 获取节点事件相关信息
        this.handleEventNodeChange(this.onStart, this.currentNode.event[this.onStart].node, false);
        this.handleEventNodeChange(this.onUpdate, this.currentNode.event[this.onUpdate].node, false);
        this.handleEventNodeChange(this.onEnd, this.currentNode.event[this.onEnd].node, false);
        this.render();
      });

      return wrapper;
    },
    generateBox(node) {
      const box = new Konva.Rect({
        name: "box",
        x: 0,
        y: 0,
        fill: BOX_FILL,
        stroke: this.currentNode?.id === node.id ? BOX_FILL_ACTIVE : BOX_FILL,
        strokeWidth: BOX_BORDER_WIDTH,
        cornerRadius: BORDER_RADIUS,
        width: BOX_WIDTH,
        height: this.getBoxHeight(node),
        shadowColor: "#111111",
        shadowBlur: 16,
        shadowOffset: { x: 2, y: 2 },
      });

      return box;
    },
    generateMain(node) {
      const mainX = (BOX_WIDTH - MAIN_WIDTH) / 2;
      const main = new Konva.Rect({
        x: mainX,
        y: MAIN_GAP,
        fill: MAIN_FILL,
        stroke: MAIN_BORDER_COLOR,
        strokeWidth: MAIN_BORDER_WIDTH,
        cornerRadius: MAIN_BORDER_RADIUS,
        width: MAIN_WIDTH,
        height: MAIN_HEIGHT,
      });

      return main;
    },
    generateRemove(node) {
      const removeGroup = new Konva.Group({
        x: BOX_WIDTH,
        y: 0,
      });
      const removeCircle = new Konva.Circle({
        x: 0,
        y: 0,
        radius: REMOVE_RADIUS,
        fill: REMOVE_CIRCLE_FILL,
        shadowColor: "black",
        shadowBlur: 8,
        shadowOffset: { x: 4, y: 4 },
      });
      const removeLineLeft = new Konva.Line({
        stroke: REMOVE_LINE_FILL,
        strokeWidth: REMOVE_LINE_WIDTH,
        points: [
          -REMOVE_RADIUS + REMOVE_PADDING,
          -REMOVE_RADIUS + REMOVE_PADDING,
          REMOVE_RADIUS - REMOVE_PADDING,
          REMOVE_RADIUS - REMOVE_PADDING,
        ],
      });
      const removeLineRight = new Konva.Line({
        stroke: REMOVE_LINE_FILL,
        strokeWidth: REMOVE_LINE_WIDTH,
        points: [
          REMOVE_RADIUS - REMOVE_PADDING,
          -REMOVE_RADIUS + REMOVE_PADDING,
          -REMOVE_RADIUS + REMOVE_PADDING,
          REMOVE_RADIUS - REMOVE_PADDING,
        ],
      });
      removeGroup.add(removeCircle);
      removeGroup.add(removeLineLeft);
      removeGroup.add(removeLineRight);

      removeGroup.on("mouseover", activeCursor);
      removeGroup.on("mouseout", defaultCursor);

      removeGroup.on("click", () => {
        this.removeNode(node);
      });

      return removeGroup;
    },
    generatePanel(node) {
      const panelX = (BOX_WIDTH - PANEL_WIDTH) / 2;
      const panelStatic = new Konva.Rect({
        x: panelX,
        y: MAIN_HEIGHT + MAIN_GAP * 2.2,
        width: PANEL_WIDTH,
        height: PANEL_HEIGHT,
        fill: PANEL_FILL,
        stroke: MAIN_BORDER_COLOR,
        strokeWidth: MAIN_BORDER_WIDTH,
        cornerRadius: PANEL_BORDER_RADIUS,
      });
      const panelDynamic = new Konva.Rect({
        x: panelX,
        y: MAIN_HEIGHT + MAIN_GAP * 2,
        width: PANEL_WIDTH,
        height: PANEL_HEIGHT,
        fill: "transparent",
        cornerRadius: PANEL_BORDER_RADIUS,
        draggable: true,
        dragBoundFunc(currentNodePos) {
          return {
            x: clamp(currentNodePos.x, CANVAS_WIDTH - PANEL_WIDTH, 0),
            y: clamp(currentNodePos.y, CANVAS_HEIGHT - PANEL_HEIGHT, 0),
          };
        },
      });

      panelDynamic.on("mouseover", activeCursor);
      panelDynamic.on("mouseout", defaultCursor);

      panelDynamic.on("dragstart", (e) => {
        dynamicArrow.setAttrs({
          x: node.x + BOX_WIDTH / 2,
          y: node.y + this.getBoxHeight(node),
          visible: true,
        });
      });
      panelDynamic.on("dragmove", (e) => {
        const { x, y } = stage.getPointerPosition();
        const centerX = node.x + BOX_WIDTH / 2;
        const centerY = node.y + this.getBoxHeight(node);
        dynamicArrow?.setAttrs({
          points: [0, 0, clamp(x, CANVAS_WIDTH, 0) - centerX, clamp(y, CANVAS_HEIGHT, 0) - centerY],
        });
      });
      panelDynamic.on("dragend", (e) => {
        // 动态面板归位
        const panelStaticAttrs = panelStatic.attrs;
        panelDynamic.setAttrs({
          x: panelStaticAttrs.x,
          y: panelStaticAttrs.y,
        });
        // 隐藏动态箭头
        dynamicArrow.visible(false);
        defaultCursor();
        this.setChild(e, node);
      });

      return [panelStatic, panelDynamic];
    },
    generateText(node) {
      const mainX = (BOX_WIDTH - MAIN_WIDTH) / 2;
      const name = new Konva.Text({
        x: mainX + TEXT_PADDING,
        text: node.name,
        fontSize: NAME_SIZE,
        fill: TEXT_FILL,
        width: MAIN_WIDTH,
        height: NAME_SIZE,
        ellipsis: true,
      });
      // 文字的大小会影响高度，所以动态设置
      name.setAttrs({
        y: MAIN_GAP * 6 - name.height(),
      });
      const index = this.preOrderIndexMap[node.id];
      const prefix = index === undefined ? "" : `${index}:`;
      const type = new Konva.Text({
        x: mainX + TEXT_PADDING,
        text: `${prefix}${node.type}`,
        fontSize: TYPE_SIZE,
        fill: TEXT_FILL,
        width: MAIN_WIDTH,
        height: TYPE_SIZE,
        ellipsis: true,
      });
      type.setAttrs({
        y: MAIN_GAP * 8.6 - type.height() / 2,
      });

      return [name, type];
    },
    generateAbort(node) {
      const abortGroup = new Konva.Group();
      const abortCircle = new Konva.Circle({
        x: 0,
        y: 0,
        radius: ABORT_RADIUS,
        fill: ABORT_FILL,
      });
      const commonCfg = {
        x: 0,
        y: 0,
        pointerLength: 5,
        pointerWidth: 8,
        stroke: ABORT_FILL,
        strokeWidth: ABORT_LINE_SIZE,
      };
      const abortArrowDown = new Konva.Arrow({
        points: [0, -1, 0, 16],
        ...commonCfg,
      });
      const abortArrowRight = new Konva.Arrow({
        points: [-1, 0, 16, 0],
        ...commonCfg,
      });

      switch (node.abortType) {
        case AbortType.LowerPriority:
          abortGroup.add(abortCircle);
          abortGroup.add(abortArrowRight);
          break;
        case AbortType.Self:
          abortGroup.add(abortCircle);
          abortGroup.add(abortArrowDown);
          break;
        case AbortType.Both:
          abortGroup.add(abortCircle);
          abortGroup.add(abortArrowRight);
          abortGroup.add(abortArrowDown);
          break;
        default:
          break;
      }
      return abortGroup;
    },
    generateBreak(node) {
      const breakGroup = new Konva.Group({
        x: BOX_WIDTH / 2,
        y: 0,
      });
      const breakCircle = new Konva.Circle({
        x: 0,
        y: 0,
        radius: REMOVE_RADIUS,
        fill: REMOVE_CIRCLE_FILL,
        shadowColor: "black",
        shadowBlur: 8,
        shadowOffset: { x: 4, y: 4 },
      });
      const breakLineLeft = new Konva.Line({
        stroke: REMOVE_LINE_FILL,
        strokeWidth: REMOVE_LINE_WIDTH,
        points: [
          -REMOVE_RADIUS + REMOVE_PADDING,
          -REMOVE_RADIUS + REMOVE_PADDING,
          REMOVE_RADIUS - REMOVE_PADDING,
          REMOVE_RADIUS - REMOVE_PADDING,
        ],
      });
      const breakLineRight = new Konva.Line({
        stroke: REMOVE_LINE_FILL,
        strokeWidth: REMOVE_LINE_WIDTH,
        points: [
          REMOVE_RADIUS - REMOVE_PADDING,
          -REMOVE_RADIUS + REMOVE_PADDING,
          -REMOVE_RADIUS + REMOVE_PADDING,
          REMOVE_RADIUS - REMOVE_PADDING,
        ],
      });
      breakGroup.add(breakCircle);
      breakGroup.add(breakLineLeft);
      breakGroup.add(breakLineRight);

      breakGroup.on("mouseover", activeCursor);
      breakGroup.on("mouseout", defaultCursor);

      breakGroup.on("click", () => {
        this.removeParent(node);
      });

      return breakGroup;
    },
    // Action和Condition不需要Panel，所以height动态计算
    getBoxHeight(node) {
      let BOX_HEIGHT = 68;
      // Root节点
      if (node.type === undefined) {
        return BOX_HEIGHT;
      }
      if (!ParentNodes.includes(node.type)) {
        BOX_HEIGHT = BOX_HEIGHT - PANEL_HEIGHT - MAIN_GAP;
      }

      return BOX_HEIGHT;
    },
    isComposite(node) {
      if (!node) {
        return false;
      }

      return CompositeNodes.includes(node.type);
    },
    showWarn(text) {
      let time = getTime();
      const content = `${time}：${text}`;
      while (this.logs.length >= 10) {
        this.logs.shift();
      }
      this.logs.push({ id: uuid(), content });
      console.warn(content);
    },
    // 视角回到root节点
    backRoot() {
      this.$refs?.scroll?.scroll((CANVAS_WIDTH - this.$refs.scroll.getBoundingClientRect().width) / 2, ROOT_Y - 50);
    },
  },
});
const panelDataMap = new WeakMap() as WeakMap<object, InstanceType<typeof component>>;
/**
 * @zh 如果希望兼容 3.3 之前的版本可以使用下方的代码
 * @en You can add the code below if you want compatibility with versions prior to 3.3
 */
// Editor.Panel.define = Editor.Panel.define || function(options: any) { return options }
module.exports = Editor.Panel.define({
  listeners: {
    show() {
      // console.log("show");
    },
    hide() {
      // console.log("hide");
    },
  },
  template: fs.readFileSync(path.join(__dirname, "../../../src/panels/static/template/default/index.html"), "utf-8"),
  style: fs.readFileSync(path.join(__dirname, "../../../src/panels/static/style/default/index.css"), "utf-8"),
  $: {
    app: "#app",
  },
  data() {
    return {};
  },
  methods: {
    initSelection() {
      const vm = panelDataMap.get(this);
      if (vm) {
        vm.initSelection();
      }
    },
  },
  ready() {
    if (this.$.app) {
      const vm = new component();
      panelDataMap.set(this, vm);
      vm.$mount(this.$.app);
    }
  },
  beforeClose() {},
  close() {
    const vm = panelDataMap.get(this);
    if (vm) {
      vm.$destroy();
    }
  },
});
